/* vim: set expandtab shiftwidth=4: -*- mode: c++; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: nil -*-
AddressBook
An intuitive address book with a novel interface
Copyright (C) 2006 Wesley Crossman
Email: wesley@crossmans.net

You can redistribute and/or modify this software under the terms of the GNU
General Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this
program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA */

#include "AddrBookScene.h"
#include <cmath>

qreal qmax(qreal n1, qreal n2) {return (n1 > n2) ? n1 : n2;}
qreal qmin(qreal n1, qreal n2) {return (n1 < n2) ? n1 : n2;}

class EntryBox : public QGraphicsRectItem {
protected:
    qreal width, height;
    QGraphicsSimpleTextItem *text;
public:
    EntryBox(QGraphicsItem *parent = NULL, QGraphicsScene *scene = NULL);
    void moveTo(qreal x, qreal y);
    void resize(qreal w, qreal h);
    void setWidth(qreal w) {resize(w, height);}
    void setHeight(qreal h) {resize(width, h);}
    void setText(QString);
    int preferredWidth() {return 150;}
    int preferredHeight() {return 150;}
    QSize preferredSize() {return QSize(preferredWidth(), preferredHeight());}
};

EntryBox::EntryBox(QGraphicsItem *parent, QGraphicsScene *scene)
        : QGraphicsRectItem(parent, scene) {
    text = new QGraphicsSimpleTextItem(this, scene);
    text->setPos(0, 0);
    width = 0;
    height = 0;
    setPen(QPen(QBrush(Qt::gray), 1, Qt::DotLine, Qt::RoundCap, Qt::RoundJoin));
    setBrush(QColor(235, 255, 255));
    setFlags(QGraphicsItem::ItemIsFocusable
            |QGraphicsItem::ItemIsSelectable);
    setZValue(-1);
}

void EntryBox::moveTo(qreal x, qreal y) {
    setPos(x, y);
}

void EntryBox::resize(qreal w, qreal h) {
    width = w;
    height = h;
    setRect(-5, -5, w+10, h+10);
}

void EntryBox::setText(QString str) {
    text->setText(str);
    QRectF r = text->boundingRect();
    setRect(-5, -5, qmax(r.width()+10, width), qmax(r.height()+10, height));
}

class DestEntryBox : public EntryBox {
protected:
    void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
public:
    DestEntryBox(QGraphicsItem *parent = NULL, QGraphicsScene *scene = NULL);
};

DestEntryBox::DestEntryBox(QGraphicsItem *parent, QGraphicsScene *scene) : EntryBox(parent, scene) {
    setAcceptDrops(true);
}

void DestEntryBox::dragEnterEvent(QGraphicsSceneDragDropEvent *event) {
    event->setAccepted(event->mimeData()->hasFormat("text/plain"));
}

class Layout : public QObject {
Q_OBJECT
protected:
    QList < EntryBox * > boxes;
    qreal width, height, margin;
public:
    Layout(qreal width, qreal height, qreal margin, QObject *parent = NULL);
    void addBox(EntryBox *box) {boxes.append(box);}
    //void setSize(int width, int height);
    //void setMargin(int);
public slots:
    void layout();
};

#include "AddrBookScene.moc"

Layout::Layout(qreal width, qreal height, qreal margin, QObject *parent)
            : QObject(parent) {
    this->width = width;
    this->height = height;
    this->margin = margin;
}

void Layout::layout() {
    //* counting rows
    bool inRow = false;
    int rowCount = 0;
    for (int a=0;a<boxes.size();++a) {
        if (inRow && !boxes[a]) {inRow = false;}
        if (!inRow && boxes[a]) {inRow = true; ++rowCount;}
    }
    
    //* setting box widths
    int rowStart = 0, colWidth = 0, colHeight = 0, totalHeight = 0;
    for (int a=0;a<boxes.size();++a) {
        if (boxes[a]) {
            if (!colWidth) rowStart = a;
            colWidth += boxes[a]->preferredWidth();
            colHeight = std::max(colHeight, boxes[a]->preferredHeight());
        } else {
            //end of row
            bool overflow = colWidth > width;
            int colCount = a-rowStart;
            qreal overflowValue = (width-rowCount*margin) / colCount;
            qreal x = 0;
            for (int b=rowStart;b<a;++b) {
                x += margin/2;
                boxes[b]->moveTo(x, 0);
                boxes[b]->setWidth(overflow ? overflowValue : boxes[b]->preferredWidth());
                if (a-1 == b) {
                    //resize last in row to absorb remaining width
                    boxes[a-1]->setWidth(width-x-margin/2);
                    break;
                }
                x += (overflow ? overflowValue : boxes[b]->preferredWidth());
                x += margin/2;
            }
            totalHeight += colHeight;
            colWidth = 0;
            colHeight = 0;
        }
    }

    //* setting box heights
    bool overflow = totalHeight > height;
    qreal overflowValue = (height-rowCount*margin) / rowCount;
    rowStart = colWidth = colHeight = 0;
    int row = 0;
    qreal y = 0;
    for (int a=0;a<boxes.size();++a) {
        if (boxes[a]) {
            if (!colWidth) {rowStart = a; y += margin/2;}
            colWidth += boxes[a]->preferredWidth();
            colHeight = std::max(colHeight, boxes[a]->preferredHeight());
        } else {
            //end of row
            for (int b=rowStart;b<a;++b) {
                boxes[b]->moveTo(boxes[b]->pos().x(), y);
                if (row == rowCount-1) {
                    boxes[b]->setHeight(height-y-margin/2);
                } else {
                    boxes[b]->setHeight(overflow ? overflowValue : colHeight);
                }
            }
            y += (overflow ? overflowValue : colHeight);
            y += margin/2;
            colWidth = 0;
            colHeight = 0;
            ++row;
        }
    }
}

class Entry : public QGraphicsRectItem {
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
public:
    QGraphicsSimpleTextItem *text;
    Entry(QGraphicsItem *parent = NULL, QGraphicsScene *scene = NULL);
    void moveTo(qreal x, qreal y);
    void setText(QString);
};

Entry::Entry(QGraphicsItem *parent, QGraphicsScene *scene)
        : QGraphicsRectItem(parent, scene) {
    text = new QGraphicsSimpleTextItem(this, scene);
    text->setPos(0, 0);
    setPen(QPen(QBrush(Qt::black), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
    setBrush(QColor(255, 255, 127));
    setFlags(QGraphicsItem::ItemIsFocusable
            |QGraphicsItem::ItemIsSelectable);
}

void Entry::moveTo(qreal x, qreal y) {
    setPos(x, y);
}

void Entry::setText(QString str) {
    text->setText(str);
    QRectF r = text->boundingRect();
    setRect(-5, -5, r.width()+10, r.height()+10);
}

void Entry::mousePressEvent(QGraphicsSceneMouseEvent *event) {
    QGraphicsRectItem::mousePressEvent(event);
    if (event->button() != Qt::LeftButton) {
        return;
    }
    QDrag *drag = new QDrag(event->widget());
    QMimeData *mime = new QMimeData;
    mime->setText(text->text());
    drag->setMimeData(mime);
    QFontMetrics metrics(QFont());
    QImage img(boundingRect().size(), QImage::Format_ARGB32_Premultiplied);
    img.fill(qRgba(128, 64, 64, 127));
    drag->setPixmap(QPixmap::fromImage(img));
    drag->setHotSpot(QPoint(16, 16));
    drag->start();
}

AddrBookScene::AddrBookScene(QObject *parent) : QGraphicsScene(parent) {
    setBackgroundBrush(QBrush(Qt::white));
    Entry *entry = NULL;
    
    Layout *layout = new Layout(640.0, 480.0, 20.0, this);

    EntryBox *entryBox = new EntryBox(NULL, this);
    entryBox->setText("Drop Box");
    layout->addBox(entryBox);
    
    DestEntryBox *toolBox = new DestEntryBox(NULL, this);
    entryBox->setText("Toolbox");
    layout->addBox(toolBox);
    
    layout->addBox(NULL);

    entry = new Entry(NULL, this);
    entry->setText("Hello World");

    //entry = new Entry(NULL, this);
    //entry->setText("Adios Amigos");

    layout->layout();

    qDebug() << "Done";
}

